package com.cskaoyan.javase.object._3equals;

/**
 * equals方法
 * 1.方法的声明
 *      public boolean equals(Object obj)
 *      平平无奇,没有什么可以注意的
 *      Object作为形参
 *          意味着方法可以传入所有的对象,所有类型的引用(或者匿名对象)都可以传入方法
 *
 * 2.方法的作用
 *      equals有相等的含义,这个方法就用来判断两个(引用所指向的)对象是否相等
 *      这两个对象指的是:
 *          a.调用equals方法的自身对象
 *          b.方法传入的对象
 *      所以该方法的作用是，用于指示其他某个对象是否与此对象"相等"。
 *      此对象即调用该equals方法的对象，其它对象即方法实参传入的对象。
 *
 * 判断对象相等,肯定是需要条件的,对象相等的条件是什么呢?
 * equals方法的具体实现,就是对象相等的条件
 *
 * 3.Object类当中默认equals方法的实现,即默认情况下,equals方法认定对象相等的条件:
 *      return (this == obj);
 *  "=="判断地址,只有当两个引用完全指向同一个对象时,才会返回true
 *  说白了,自己和自己才相等,这是一个非常苛刻的条件.
 *
 *  4.实际开发中,我们往往不需要这么苛刻的对象相等的条件,我们认为的(一般认为的)对象相等应该是:
 *      a.如果不是同种类型对象,没有可比性,直接认为对象不相等
 *      b.如果是同种类型对象,那么它们就可以比较了,这时如果它们的属性取值一样,就可以认为对象相等
 *          不需要完全是一个对象,那么苛刻的条件
 *      当然对象相等是一个相等的规则,具体可以自定义.
 *
 * 5.JDK文档中,说明的equals方法的重写原则/规则:
 *  equals方法重写的常规协定：
 *      1.自反性：对于任何非空引用值 x，x.equals(x)都应返回 true
 *
 *      2.排他性：当比对的不是同种类型的对象或者是一个null时，默认返回false
 *      3.对称性：对于任何非空引用值 x 和 y，当且仅当y.equals(x) 返回 true 时，x.equals(y) 才应返回true.
 *      String类的equals方法
 *      a.equals(b)
 *      b.equals(a)
 *      应该返回一个相同的结果
 *
 *      4.传递性：对于任何非空引用值 x、y 和 z，如果x.equals(y)返回 true，并且 y.equals(z) 返回 true，那么x.equals(z) 应返回 true。
 *      x = y
 *      y = z
 *      ->
 *      x = z
 *
 *      5.一致性：对于任何非空引用值 x 和 y，多次调用 x.equals(y)始终返回 true 或始终返回 false。前提是,对对象做相等判断的条件没有改变.
 *
 * 只需要按照对象的成员变量取值来判断对象相等,传递性,一致性,对称性就自动满足了.
 *
 *
 * equals方法比较对象相等,使用细节:
 *      1.equals方法在判断排它性时,可以用两种方式:
 *          a.getClass
 *              只能接受自身对象传入方法
 *          b.instanceof关键字
 *              可以允许接受子类对象传入方法
 *
 *      2.equals方法格式十分固定,实际开发中多数会选择快捷键自动生成
 *          alt + insert
 *
 *
 *      3.浮点数在比较大小时,使用"=="在绝大多数情况下是没有问题的,但是浮点数有无穷大 无穷小 非数等特殊概念,一旦出现这种情况,"=="号比较就不行了
 *      所以出于逻辑严谨的考虑
 *      比较浮点数大小,不建议用"=="
 *      而是用对应包装类型的compare方法
 *
 *      包括Double.compare(double1,double2)
 *      以及Float.compare(float1,float2)
 *      这两个compare方法会返回一个整数
 *      可以把该方法看成左边的参数 - 右边的参数
 *      当方法返回大于0的数时,表示double1 > double2
 *      当方法返回小于0的数时,表示double1 < double2
 *      只有当方法返回0时,表示double1 = double2
 *
 *      4.浮点数在计算时,只是一个近似运算,不是一个精确运算,为了精确计算小数(比如在财务系统中)
 *      推荐使用类BigDecimal
 *
 *      5.如果类中有引用数据类型成员变量，需要去调用它们的equals方法完成比较。这就意味着还需要重写这个类的equals方法。
 *
 * @since 14:35
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        Student s1 = new Student(18, 100);
        Student s2 = new Student(18, 100);
        Student s3 = s1;
        System.out.println(s1 == s2);
        System.out.println(s1.equals(s2));
        System.out.println(s1 == s3);
        System.out.println(s1.equals(s3));
    }
}

class Student {
    int age;
    double score;
    String name;

    public Student() {
    }

    public Student(int age, double score) {
        this.age = age;
        this.score = score;
    }

    // 手动重写equals方法
    /*@Override
    public boolean equals(Object obj) {
        // 满足自反性,自己和自己比返回true
        if (this == obj) {
            // 说明是同一个对象
            return true;
        }
        // 满足排它性
        if (obj == null || this.getClass() != obj.getClass()) {
            // 如果方法传入的是null或者不是同种类型对象,不具有可比性,直接返回false
            return false;
        }
        *//*
            接下来的:
                对称性
                传递性
                一致性
            只需要按照对象的成员变量取值
            来判断对象相等
            就自动满足以上三点
         *//*
        // 代码运行到这里,obj一定不是null,也一定不和自身对象是一个对象,且一定是同种类型对象
        // obj需要强转成Student引用,无需判断了,因为上面已经判断过了
        Student s = (Student) obj;
        // 首先比较年龄
        if (this.age != s.age) {
            // 年龄不相等,对象就不相等
            return false;
        }

        // 运行到这里,年龄相等了,最后就判断分数
        return this.score == s.score;
    }*/

    /*@Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (age != student.age) return false;
        return Double.compare(student.score, score) == 0;
    }*/

   /* @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Student)) return false;

        Student student = (Student) o;

        if (age != student.age) return false;
        return Double.compare(student.score, score) == 0;
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        result = age;
        temp = Double.doubleToLongBits(score);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        return result;
    }*/

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (age != student.age) return false;
        if (Double.compare(student.score, score) != 0) return false;
        return name != null ? name.equals(student.name) : student.name == null;
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        result = age;
        temp = Double.doubleToLongBits(score);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        result = 31 * result + (name != null ? name.hashCode() : 0);
        return result;
    }
}